/* Copyright 2009 Requirements Management System
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.mags.remas.model.commons.checker;

import java.io.BufferedReader;  
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.validator.GenericValidator;
import org.mags.remas.exception.FileMessageException;
import org.mags.remas.freemarker.BufferedTextTemplateLoader;

import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

/**
 * 
 * @author Mario Gaitan
 *
 */
public abstract class AbstractDataBaseChecker implements IDataBaseChecker {
	private final String driverName;
	private IShowProgress showProgress;
	private SQLUtils sqlUtils;
	
	public IShowProgress getShowProgress() {
		return showProgress;
	}
	public void setShowProgress(IShowProgress showProgress) {
		this.showProgress = showProgress;
	}
	public String getDriverName() {
		return driverName;
	}
	public AbstractDataBaseChecker(String driverName) {
		if(GenericValidator.isBlankOrNull(driverName))
			throw new RuntimeException("driver name can't be null");
		this.driverName = driverName;
	}
	@Override
	public Throwable testConnection(BasicDataSource dataSource) {
		Connection connection=null;
		try {
			connection = dataSource.getConnection();
		} catch (Exception e) {
			return (Throwable)e;
		} finally {
			if(connection!=null) {
				try {
					connection.close();
				} catch (Exception e) {
					/*IGNORE EXCEPTION*/
				}
			}
		}
		return null;
	}
	@Override
	public Throwable createDatabase(BasicDataSource dataSource) {
		return null;
	}
	@Override
	public Throwable updateDataStructure(BasicDataSource dataSource) {
		try {
			String text = executeScript(dataSource, driverName+".update.ftl");
			if(!GenericValidator.isBlankOrNull(text))
				return new FileMessageException("exception on update data structure", text);
		} catch(Exception e) {
			return e;
		}
		return null;
	}
	public SQLUtils getSQLUtils(Connection connection) {
		if(sqlUtils==null)
			sqlUtils = new SQLUtils(connection);
		sqlUtils.setConnection(connection);
		return sqlUtils;
	}
	protected String executeScript(BasicDataSource dataSource, String fileName) {
		Connection connection = null;
		try {
			String script = ScriptReader.readScript(fileName);
			if(GenericValidator.isBlankOrNull(script))
				return null;
			connection = dataSource.getConnection();
			Map<String, String> templateContents = new HashMap<String, String>();
			templateContents.put("script", script);
			Configuration configuration = new Configuration();
			TemplateLoader templateLoader = new BufferedTextTemplateLoader(templateContents);
			configuration.setTemplateLoader(templateLoader);
			configuration.setObjectWrapper(new DefaultObjectWrapper());
			StringWriter out = new StringWriter();
			Template temp = configuration.getTemplate("script");
			Map<String, Object> context = new HashMap<String, Object>();
			context.put("sqlUtils", getSQLUtils(connection));
			temp.process(context, out);
			String _out = out.toString();
			_out=_out==null?"":
			_out.replace("\t", "").replace("\r", "").trim();
			return _out;
		} catch (Exception e) {
			if(e instanceof RuntimeException)
				throw (RuntimeException)e;
			else
				throw new RuntimeException(e);
		} finally {
			try {
				if(connection!=null)
					connection.close();
			} catch(Exception e) {
				/*IGNORE EXCEPTION*/
			}
		}
	}
	@Override
	public boolean equalsDriverName(String driverName) {
		return driverName.equalsIgnoreCase(driverName);	
	}
	@Override
	public Throwable createDataStructure(BasicDataSource dataSource) {
		Connection connection = null;
		BufferedReader reader = null;
		try {
			String out = executeScript(dataSource, driverName+".pre.ftl");
			if(!GenericValidator.isBlankOrNull(out))
				return new FileMessageException("exception on execute pre installation script", out);			
			connection = dataSource.getConnection();
			Statement statement = connection.createStatement();
			SQLScriptReader scriptReader = new SQLScriptReader();
			scriptReader.loadScript(driverName+".sql");
			String st;
			boolean inBatch = false;
			do {
				st = scriptReader.getNextStatement();
				if(!GenericValidator.isBlankOrNull(st)) {
					if(inBatch)
						statement.addBatch(st);
					else
						statement.execute(st);
					if(showProgress!=null) {
						float position = scriptReader.getPointer();
						float total = scriptReader.getScript().length();
						float progress = ((position/total)*100);
						if(progress>99)progress=99;
						showProgress.showProgress((int)progress, st);
					}
				}
			} while(st!=null);
			if(inBatch)
				statement.executeBatch();
			if(showProgress!=null)
				showProgress.showProgress(100, null);
			out = executeScript(dataSource, driverName+".post.ftl");
			if(!GenericValidator.isBlankOrNull(out))
				return new FileMessageException("exception on execute post installation script", out);			
		} catch (Exception e) {
			return e;
		} finally {
			try {
				if(connection!=null)
					connection.close();
			} catch(Exception e) {/*IGNORE EXCEPTION*/}
			try {
				if(reader!=null)
					reader.close();
			} catch(Exception ex){/*IGNORE EXCEPTION*/}
		}
		return null;
	}
}
